# 第二章：深度学习通用流程 - 可靠的文件下载教学
import os
import torch
import torch.hub as hub  # 用于下载文件 
from torchvision.datasets.utils import download_url # 另一种下载方法 需要安装
import requests # 备用下载方法 # 需要安装 pip install requests
import certifi

# 1. 设置安全下载环境
print("步骤1: 设置安全下载环境")
os.environ['SSL_CERT_FILE'] = certifi.where()
print(f"✅ SSL证书路径设置: {certifi.where()}")

# 2. 选择下载方法
def download_file(url, save_dir, method='torch_hub'):
    """可靠的文件下载函数"""
    os.makedirs(save_dir, exist_ok=True) # 确保保存目录存在
    filename = os.path.basename(url) # 从URL提取文件名
    save_path = os.path.join(save_dir, filename)   # 拼合保存路径
    
    print(f"\n下载资源: {url}")
    print(f"保存位置: {save_path}")
    
    try:
        if method == 'torch_hub':
            # 方法1: 使用torch.hub下载（最可靠）
            print("使用方法: torch.hub.download_url_to_file")
            hub.download_url_to_file(url, save_path)
            
        elif method == 'torchvision':
            # 方法2: 使用torchvision的下载工具
            print("使用方法: torchvision.datasets.utils.download_url")
            download_url(url, save_dir, filename=filename)
            
        elif method == 'requests':
            # 方法3: 使用requests作为备选
            print("使用方法: requests")
            response = requests.get(url, stream=True, timeout=30) # 采用流式下载，适合大文件
            response.raise_for_status() # 检查请求是否成功
            
            with open(save_path, 'wb') as f: 
                for chunk in response.iter_content(chunk_size=8192):# 表示将响应内容按 8192 字节（8KB） 为单位拆分成多个 “数据块”（chunk），并逐个返回。
                    if chunk:
                        f.write(chunk) # 写入非空数据
        
        # 验证下载
        if os.path.exists(save_path) and os.path.getsize(save_path) > 0:
            size_kb = os.path.getsize(save_path) / 1024
            print(f"✅ 下载成功! 文件大小: {size_kb:.2f} KB")
            return True
        else:
            print("❌ 下载失败: 文件为空或不存在")
            return False
            
    except Exception as e:
        print(f"❌ 下载失败: {str(e)}")
        return False

# 3. 教学演示
def main():
    """主函数：演示多种下载方法"""
    print("\n" + "="*50)
    print("PyTorch文件下载教学")
    print("="*50)
    
    # 可靠的下载资源列表
    resources = {
        "1": {
            "name": "Transformer论文",
            "url": "https://arxiv.org/pdf/1706.03762.pdf",
            "description": "Attention Is All You Need - 深度学习里程碑论文"
        },
        "2": {
            "name": "PyTorch教程数据集",
            "url": "https://download.pytorch.org/tutorial/data.zip",
            "description": "用于PyTorch入门教程的示例数据"
        },
        "3": {
            "name": "示例文本文件",
            "url": "https://raw.githubusercontent.com/pytorch/tutorials/master/beginner_source/README.md",
            "description": "PyTorch教程的README文件"# 可能会由于外网的原因下载不了
        },
        "4": {
            "name": "CIFAR-10数据集",
            "url": "https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz",
            "description": "常用的图像分类数据集"   
        },
        "5": {
            "name": "猫狗分类数据集",
            "url": "https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip",# 可能会由于外网的原因下载不了
            "description": "用于图像分类的猫狗数据集"
        }
    
    }
    
    print("\n可用下载资源:")
    for key, res in resources.items():
        print(f"{key}. {res['name']} - {res['description']}")
    
    choice = input("\n请选择下载内容(1/2/3/4/5): ").strip() #strip()去掉首尾空格
    selected = resources.get(choice, resources["1"]) #这句话的作用是从字典 resources 中获取指定键对应的值，若指定键不存在，则使用默认键 "1" 对应的值。
    
    # 设置保存目录
    save_dir = os.path.join(os.path.expanduser("~"), "Desktop", "Pytorch与深度学习实战","code","第二章","tmp") #os.path.expanduser("~")获取当前用户的主目录（家目录）路径
    print(f"\n文件将保存到: {save_dir}")
    
    # 尝试多种下载方法
    methods = ['torch_hub', 'torchvision', 'requests']
    
    print("\n下载方法演示:")
    for i, method in enumerate(methods, 1):
        print(f"\n方法 {i}: {method}")
        success = download_file(selected["url"], save_dir, method=method)
        
        if success:
            print(f"✅ {method} 方法成功!")
            break
        else:
            print(f"⚠️ {method} 方法失败，尝试下一种方法")
    
    print("\n教学总结:")
    print("1. 文件下载是深度学习工作流的第一步")
    print("2. PyTorch提供多种下载工具:")
    print("   - torch.hub.download_url_to_file (最可靠)")
    print("   - torchvision.datasets.utils.download_url")
    print("   - requests库 (作为备选)")
    print("3. 选择方法应考虑:")
    print("   a) 网络环境")
    print("   b) 文件大小")
    print("   c) 错误处理需求")
    
    if not success:
        print("\n⚠️ 所有下载方法均失败，建议:")
        print("1. 检查网络连接")
        print("2. 手动下载: " + selected["url"])
        print("3. 将文件保存到: " + save_dir)

if __name__ == "__main__":
    main()
